




import java.io.IOException;
import java.util.Scanner;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.swing.UIManager;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Laurens
 */
public class Introduce_Local_Extension_Refactor implements RefactoringMethod
{
    /*
     * Variable declaration
     */
    int lineNumber = 0;
    String superClassName = "empty";
    FileEditor fileE = new FileEditor();
    //Holds the list of characters
    characterList characters = new characterList(); 
    //Holds certain error messages
    ErrorMessages error = new ErrorMessages();
    //corrects the file into a well formed file
    CorrectFile correct = new CorrectFile();
    String newPath = "";
    //type is either class or interface
    String type = "class";
    String callSuper = "none";
    /*
     * End of variable declaration
     */
    
    /**
     * Constructor that does nothing
     */
    public Introduce_Local_Extension_Refactor()
    {
        //DOES NOTHING
    }
    /*
     * End of constructor
     */
    
    /**
     * Does the introduce location extension refactor
     * @param parameters, which include the path
     * @return 
     */
    @Override
    public Vector<String> refactor(Vector<String> parameters)
    {
        /*
         * Creates same look and feel for GUI
         */
        try 
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } 
        catch (Exception ex) 
        {
            Logger.getLogger(Rename_Parameter_Refactor.class.getName()).log(Level.SEVERE, null, ex);
        } 
        /*
         * End of same look and feel
         */
        
        Vector<String> result = new Vector<String>();
        String path = "";
        String newClass = "";
        String _newClass = "";
        String file = "";
        String uncorrectedFile = "";
        boolean noMatch = false;
        boolean noPunctuation = false;
        boolean keywords = false;
        
        /*
         * Parameters declaration
         */
        if(parameters.size() > 0)
        {
            if(parameters.get(1) == null)
            {
                //Input from user
                _newClass = JOptionPane.showInputDialog("What is the class name you would like to add?\n");
            }
            else
            {
                //Input from other programmer
                _newClass = parameters.get(1);
            }
            path = parameters.get(0);
            result.add(path);
            newClass = _newClass.trim();
            
            /*
             * Checks to make sure that the edit box is not empty 
             */
            if("".equals(newClass))
            {
                result.add("Error, you may not leave this edit box empty.\nPlease enter a new class name.");
                return result;
            }
            
            /*
             * NewClass name cant be a keyword or a punctuation. 
             */
            keywords = characters.checkKeywords(newClass);
            noPunctuation = characters.punctuationCheck(newClass);
            try
            {
                /*
                 * Reads the file needed to do the introduce local extension refactor
                 * Reads in and then corrects the file
                 */
                uncorrectedFile = fileE.read(path);
                file = correct.returnCorrectFile(uncorrectedFile);
                Scanner scanner = new Scanner(file);
                /*
                 * Code to get superClassName
                 */
                boolean found = false;
                while(!found)
                {
                    String currentLine = scanner.nextLine();
                    if(currentLine.contains(" public "))
                    {
                        //class can either be class or interface
                        if(currentLine.contains(" class "))
                        {
                            superClassName = getSuperClassName(currentLine);
                            if(!" ".equals(superClassName))
                            {
                                //Once found leave the while loop
                                found = true;
                                type = "class";
                            }
                        }
                        //class can either be class or interface
                        else if(currentLine.contains(" interface "))
                        {
                            superClassName = getSuperClassName(currentLine);
                            if(!" ".equals(superClassName))
                            {
                                //Once found leave the while loop
                                found = true;
                                type = "interface";
                            }
                        }
                    }
                    else if(currentLine.contains("public "))
                    {
                        //class can either be class or interface
                        if(currentLine.contains(" class "))
                        {
                            //Once found leave the while loop
                            superClassName = getSuperClassName(currentLine);
                            if(!" ".equals(superClassName))
                            {
                                found = true;
                                type = "class";
                            }
                        }
                        //class can either be class or interface
                        else if(currentLine.contains(" interface "))
                        {
                            //Once found leave the while loop
                            superClassName = getSuperClassName(currentLine);
                            if(!" ".equals(superClassName))
                            {
                                found = true;
                                type = "interface";
                            }
                        }
                    }
                }
                //If it is a class, then it might have a constructor and
                //the constructor must be called in the subclass
                if("class".equals(type))
                {
                    boolean constructor = false;
                    //Checks if there is a constructor
                    constructor = hasConstructor(file);
                    if(constructor)
                    {
                        //Gets the superclass constructor call with all the parameters
                        String param = getSuperFunctionCall(file);
                        callSuper = param.substring(0, param.length() - 1);
                    }
                }
                else
                {
                    //Interface does not have a constructor call
                    
                }
                //Returns the length of the file
                lineNumber = findLineNumber(file);
                
                /*
                 * Check if newClass is being used inside the class already
                 */
                noMatch = checkMatches(newClass, file);
                //Checks if newClass same as superclass
                if(superClassName == null ? newClass == null : superClassName.equals(newClass))
                {
                    //Returns an error if that is the case
                    result.add("Error, there is a clash between superclass name and your entered value!\nPlease try another one.\n");
                    return result;
                }
                else
                {
                    //Checks if punctuation is used in newClass, not allowed
                    if(noPunctuation)
                    {
                        //returns punctuation error
                        result.add(error.getErrorMessages("punctuation"));
                        return result;
                    }
                    //Checks if a keyword is used in newClass, not allowed
                    else if(keywords)
                    {
                        //returns keyword error
                        result.add(error.getErrorMessages("keyword"));
                        return result;
                    }        
                    //Checks if there's a match used of newClass with any variable in 
                    //current class, not allowed
                    else if(noMatch)
                    {
                        //returns alreadyUsed error
                        result.add(error.getErrorMessages("alreadyUsed"));
                        return result;
                    }
                    else
                    {
                        /*
                        * Checks if the user wants an inline assignment or a new class
                        */
                        String inLine = "";
                        Object[] choice = new Object[2];
                        //Choices between inLine and newClass
                        choice[0] = "In Line";
                        choice[1] = "New Class";
                        //Get input from user
                        inLine = JOptionPane.showInputDialog(null, "You can choose between creating inLine or a new Class?", "Choice Selection", JOptionPane.QUESTION_MESSAGE, null, choice,"").toString();
                        //If inLine accepted do function replaceInLine
                        if("In Line".equals(inLine))
                        {
                            result.add(replaceInline(newClass, file));
                        }
                        //Else newClass accepted do function replaceOtherFile
                        else
                        {
                            //result.get(1) = currentFile unchanged
                            result.add(file);
                            //result.get(2) = new path of new file created
                            result.add(newPath);
                            //get imports for new file
                            Vector<String> imports = getImports(file);
                            //create new  file, with imports from current class
                            result.add(replaceOtherFile(newClass, file, imports));
                        }
                    }
                }
            }
            catch(Exception e)
            {
                e.printStackTrace();
                result.add(error.getErrorMessages("tryFailed"));
                return result;
            }
        }
        return result;
    }
    
    /**
     * Creates the heading for the new class
     * @param className
     * @return 
     */
    public String replaceClassName(String className)
    {
        String result = "";
        //Type = class/interface
        //ClassName = old ClassName
        //SuperClassName = new ClassName
        result = "public " + type + " " + className + " extends " +  superClassName;
        return result;
    }
    
    /**
     * Inserts a new class inside the previous super class
     * @param className
     * @param file
     * @return 
     */
    private String replaceInline(String className, String file)
    {
        Scanner scanner = new Scanner(file);
        Vector<String> result = new Vector<String>();
        int j = 1;
        //Gets all the abstract methods that need to be implemented
        Vector<String> abstractMethod = abstractMethod(file);
        while(scanner.hasNext())
        {
            /*
             * reads in the previous file until you reach the last line before the last }
             */
            String currentLine = scanner.nextLine();
            result.add(currentLine);
            //Once you reach the line of the last }, you add the new class
            if(j == lineNumber - 1)
            {
                /*
                 * Everything needed inside the class
                 */
                result.add("\n");
                //Class declaration
                result.add("\t"+replaceClassName(className));
                result.add("\t{");
                if("class".equals(type))
                {
                    //Constructor creation
                    result.add("\tpublic "+className+"()");
                    result.add("\t{");
                    //Checks if callSuper = ""
                    if("none".equals(callSuper))
                    {
                        result.add("\t}");
                    }
                    if("".equals(callSuper))
                    {
                        result.add("\n\t\tsuper();");
                        result.add("\t}");
                    }
                    else
                    {
                        //Call superclass's constructor
                        result.add("\n\t\tsuper(" + callSuper + ");");
                        result.add("\t}");
                    }
                }
                else
                {
                    
                }
                result.add("\n");
                //Add in all the abstract methods
                for(int k = 0; k < abstractMethod.size(); k++)
                {
                    //Override needed on top of all abstract methods
                    result.add("\t\t@Override");
                    //Each abstract method called
                    result.add("\t\t"+abstractMethod.get(k).toString());
                    result.add("\t\t{");
                    //No implementation
                    result.add("\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");");
                    result.add("\t\t}");
                }
                result.add("\t}");
            }
            j++;
        }
        //Changing the vector into a string output
        String output = " ";
        for(int u = 0; u < result.size(); u++)
        {
            output += result.get(u) + "\n";
        }
        return output;
    }
    
    /**
     * Creates a new file and inserts the neccessary functionality inside of it
     * @param className
     * @param file
     * @return
     * @throws IOException 
     */
    private String replaceOtherFile(String className, String file, Vector<String> imports) throws IOException
    {
        Vector<String> result = new Vector<String>();
        //Gets the abstract method needed to be implemented
        Vector abstractMethod = abstractMethod(file);
        //Inserts the package if there is one
        result.add(getPackage(file));
        //Searching through all imports needed, adding them to the result vector
        for(int y = 0; y <  imports.size(); y++)
        {
            result.add(imports.get(y));
        }
        //Comments that are placed before the start of the class
        result.add("/*");
        result.add("* To change this template, choose Tools | Templates");
        result.add("* and open the template in the editor.");
        result.add("*/");
        result.add("\n");
        //Class declaration
        result.add(replaceClassName(className));
        result.add("{");
        if("class".equals(type))
        {
            //Constructor creation
            result.add("\tpublic "+className+"()");
            result.add("\t{");
            //Checks if callSuper = ""
            if("".equals(callSuper))
            {
                //Does nothing
            }
            else
            {
                //Call superclass's constructor
                result.add("\n\t\tsuper(" + callSuper + ");");
                result.add("\t}");
            }
        }
        result.add("\n");
        for(int k = 0; k < abstractMethod.size(); k++)
        {
            //Override needed for abstract methods
            result.add("@Override");
            //Calling all of the abstract methods from superclass
            result.add("\t"+abstractMethod.get(k).toString());
            result.add("\t{");
            //No implementation
            result.add("\n\t\tthrow new UnsupportedOperationException(\"Not yet implemented\");");
            result.add("\t}");
        }
        result.add("}");
        //Changing the vector into a string output
        String output = " ";
        for(int u = 0; u < result.size(); u++)
        {
            output += result.get(u) + "\n";
        }
        //Return the output, new file will be written by in the gui
        return output;
    }
    
    /**
     * Gets the name of the superclass
     * @param currentLine
     * @return 
     */
    public String getSuperClassName(String currentLine)
    {
        StringTokenizer st = new StringTokenizer(currentLine, " ");
        String result = "";
        while(st.hasMoreTokens())
        {
            if("".equals(result))
            {
                String current = st.nextToken();
                if("public".equals(current))
                {
                    
                }
                else if("class".equals(current))
                {
                    
                }
                else if("abstract".equals(current))
                {
                    
                }    
                else if("interface".equals(current))
                {
                    
                }   
                //If current not equal to all of the above then next token must be the class name
                else
                {
                    result = current;
                }
            }
            else
            {
                st.nextToken();
            }
        }
        //returns the class/interface name
        return result;
    }
    
    /**
     * Determines the lineNumber of the last line where the class may be inlined inserted
     * @param file
     * @return 
     */
    public int findLineNumber(String file)
    {
        Scanner scanner = new Scanner(file);
        int i = 1;
        //Openbrackets found {
        int openBrackets = 0;
        //Closedbrackets found }
        int closedBrackets = 0;
        int temp = 1;
        while(scanner.hasNext())
        {
            String currentLine = scanner.nextLine();
            //If { found then openBrackets must increase
            if(currentLine.contains("{"))
            {
                openBrackets++;
            }
            //If } found then closedBrackets must increase
            else if(currentLine.contains("}"))
            {
                closedBrackets++;
            }
            else
            {
                
            }
            if(openBrackets != 0)
            {
                //Until openBrackets == closedBrackets keep searching for last }
                if(openBrackets == closedBrackets)
                {
                    //Last line where } is found
                    temp = i;
                }
            }
            i++;
        }
        return temp;
    }
    
    /**
     * Takes all the abstract declared methods and stores them inside a vector
     * @param file
     * @return 
     */
    public Vector<String> abstractMethod(String file)
    {
        Scanner scanner = new Scanner(file);
        Vector<String> result = new Vector<String>();
        while(scanner.hasNext())
        {
            String currentLine = scanner.nextLine();
            if(currentLine.contains(" class "))
            {
                
            }
            else if(currentLine.contains(" interface "))
            {
                
            }
            else
            {
                /*
                 * Any line that contains abstract must be put into the result
                 */
                if(currentLine.contains(" abstract "))
                {
                    result.add(currentLine);
                }
            }
        }
        //Format the result, to remove the abstract in the line
        Vector<String> formatResult = new Vector<String>();
        for(int u = 0; u < result.size(); u++)
        {
            StringTokenizer string = new StringTokenizer(result.get(u));
            String output = "";
            boolean hasSemiColon = false;
            //Checks if there is a semi colon at the end of the function call
            if(result.get(u).contains(");"))
            {
                hasSemiColon = true;
            }
            while(string.hasMoreTokens())
            {
                String current = string.nextToken();
                //Remove abstract from the line
                if(current.equals("abstract"))
                {
                    
                }
                //Remove abstract from the line
                else if(current.equals(" abstract"))
                {

                }
                //Remove abstract from the line
                else if(current.equals("abstract "))
                {

                }
                //Remove abstract from the line
                else if(current.equals(" abstract "))
                {
                    
                }
                else
                {
                    //Add in the rest of the line into the output
                    output += current + " ";
                    
                }
            }
            if(hasSemiColon)
            {
                //Removes the ; from the function
                formatResult.add(output.substring(0, output.length() - 2));
            }
            else
            {
                formatResult.add(output);
            }
        }
        return formatResult;
    }
    
    /**
     * Gets the package name to insert in the new class
     * @param file
     * @return 
     */
    public String getPackage(String file)
    {
        Scanner scanner = new Scanner(file);
        boolean classFound = false;
        boolean packageFound = false;
        String output = "";
        while(!classFound)
        {
            String currentLine = scanner.nextLine();
            if(currentLine.contains(" class "))
            {
                //Once class found can exit loop, package always found before class
                classFound = true;
            }
            else if(currentLine.contains(" interface "))
            {
                //Once class found can exit loop, package always found before class
                classFound = true;
            }
            //Else gets the package
            else if(currentLine.contains("package "))
            {
                packageFound = true;
                output += currentLine;
                classFound = true;
            }
            else if(currentLine.contains("package"))
            {
                packageFound = true;
                output += currentLine;
                classFound = true;
            }
            else if(currentLine.contains(" package"))
            {
                packageFound = true;
                output += currentLine;
                classFound = true;
            }
            else if(currentLine.contains(" package "))
            {
                packageFound = true;
                output += currentLine;
                classFound = true;
            }
            else
            {
                
            }
        }
        if(packageFound)
        {
            return output;
        }
        else
        {
            //No package found, "" return
            return "";
        }
    }

    /**
     * Returns true if there is a match of newClass in the file, works with comments
     * @param newClass
     * @param file
     * @return 
     */
    private boolean checkMatches(String newClass, String file) 
    {
        
        Scanner scanner = new Scanner(file);
        boolean result = false;
        Vector<String> comment = new Vector<String>();
        /*
         * Checking for multiple stars in the comment vector
         */
        comment.add("");
        comment.add("*");
        comment.add("**");
        comment.add("***");
        comment.add("****");
        comment.add("*****");
        comment.add("******");
        comment.add("*******");
        comment.add("********");
        comment.add("*********");
        while(scanner.hasNext())
        {
            String currentLine = scanner.nextLine();
            if(currentLine.contains("*/"))
            {
                //Checks all values except those inside comments
                String sub = currentLine.substring(currentLine.indexOf("*/"));
                int length = currentLine.substring(currentLine.indexOf("*/")).length();
                if(length != 0)
                {
                    //Checks for all places where newClass is found
                    for(int o = 0; o < characters.characterListMethod().size(); o++)
                    {
                        for(int p = 0; p < characters.characterListMethod().size(); p++)
                        {
                            if(sub.contains(characters.characterListMethod().get(o)+newClass+characters.characterListMethod().get(p)))
                            {
                                result = true;
                                return result;
                            }
                            else
                            {
                                result = false;
                            }
                        }            
                    }
                }   
            }
            //Doesnt check inside comments
            else if(currentLine.contains("/*"))
            {
                for(int t = 0; t < comment.size(); t++)
                {
                    if(currentLine.contains("/*"+comment.get(t)))
                    {
                        if(currentLine.contains("*/"))
                        {
                            t = comment.size();
                        }
                        else
                        {
                            while(!scanner.nextLine().contains("*/"))
                            {
                                
                            }
                            t = comment.size();
                        }
                    }
                }
            }
            //Doesnt check for // comments
            else if(currentLine.equals("//"))
            {
                
            }
            else
            {
                //Checks for all places where newClass is found
                for(int o = 0; o < characters.characterListMethod().size(); o++)
                {
                    for(int p = 0; p < characters.characterListMethod().size(); p++)
                    {
                        if(currentLine.contains(characters.characterListMethod().get(o)+newClass+characters.characterListMethod().get(p)))
                        {
                            result = true;
                            return result;
                        }
                        else
                        {
                            result = false;
                        }
                    }            
                }
            }
        }
        return result;
    }
    
    /**
     * Returns all the imports of superclass
     * @param file
     * @return 
     */
    public Vector<String> getImports(String file)
    {
        Scanner scanner = new Scanner(file);
        Vector<String> result = new Vector<String>();
        boolean classFound = false;
        while(!classFound)
        {
            String currentLine = scanner.nextLine();
            if(currentLine.contains(" import "))
            {
                //checks the line for import
                result.add(currentLine);
            }
            if(currentLine.contains("import "))
            {
                //checks the line for import
                result.add(currentLine);
            }
            else if(currentLine.contains(" class "))
            {
                //Once the class is found stop looking for imports
                classFound = true;
            }
        }
        return result;
    }

    /**
     * Returns a description of the current refactoring method
     * @return 
     */
    @Override
    public String description() 
    {
        //Returns the description of Introduce local extension
        return "Creates a new class that extends from the current class. It implements all the abstract functions of the current class.";
    }    
    
    /**
     * Returns the parameters called in the super function calls
     * @param file
     * @return
     */
    public String getSuperFunctionCall(String file)
    {
        Scanner scanner = new Scanner(file);
        String result  = "";
        while(scanner.hasNext())
        {
            String currentLine = scanner.nextLine();
            //Dont check the class line for anything
            if(currentLine.contains(" class "))
            {
                
            }   
            //Once superClassName found you know its the constructor, begin searching the parameter list
            else if(currentLine.contains(" "+superClassName+"("))
            {
                //Checks for the parameter list
                StringTokenizer st = new StringTokenizer(currentLine, "(");
                //Removes the first token();
                st.nextToken();
                String current = "";
                while(st.hasMoreTokens())
                {
                    current += st.nextToken();
                }
                //Scan through the parameter list
                StringTokenizer st2 = new StringTokenizer(current, ",");
                while(st2.hasMoreTokens())
                {
                    String current2 = st2.nextToken();
                    //Checks for primitive types
                    if(current2.contains("int "))
                    {
                        result += "0,";
                    }
                    else if(current2.contains("double "))
                    {
                        result += "0.0,";
                    }
                    else if(current2.contains("float "))
                    {
                        result += "0.0,";
                    }
                    else if(current2.contains("Integer "))
                    {
                        result += "0.0,";
                    }
                    else if(current2.contains("String "))
                    {
                        result += "\"\",";
                    }
                    //Checks for complex types
                    else if(current2.contains("Vector "))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("Object "))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("String[ "))
                    {
                        result += "null,";
                    }
                            else if(current2.contains("String []"))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("String[] "))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("String [ "))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("["))
                    {
                        result += "\null,";
                    }
                    //Checks for all Vector types
                    else if(current2.contains("Vector<Object>"))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("Vector<Integer>"))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("Vector<String>"))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("Vector<"))
                    {
                        result += "null,";
                    }
                    else 
                    {
                        result += " ";
                    }
                }
            }
            else if(currentLine.contains(" "+superClassName+" ("))
            {
                StringTokenizer st = new StringTokenizer(currentLine, "(");
                st.nextToken();
                String current = "";
                while(st.hasMoreTokens())
                {
                    current += st.nextToken();
                }
                StringTokenizer st2 = new StringTokenizer(current, ",");
                while(st2.hasMoreTokens())
                {
                    String current2 = st2.nextToken();
                    //Checks for primitive types
                    if(current2.contains("int "))
                    {
                        result += "0,";
                    }
                    else if(current2.contains("double "))
                    {
                        result += "0.0,";
                    }
                    else if(current2.contains("float "))
                    {
                        result += "0.0,";
                    }
                    else if(current2.contains("Integer "))
                    {
                        result += "0.0,";
                    }
                    else if(current2.contains("String "))
                    {
                        result += "\"\",";
                    }
                    //Checks for complex types
                    else if(current2.contains("Vector "))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("Object "))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("String[ "))
                    {
                        result += "null,";
                    }
                            else if(current2.contains("String []"))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("String[] "))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("String [ "))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("["))
                    {
                        result += "\null,";
                    }
                    //Checks for all Vector types
                    else if(current2.contains("Vector<Object>"))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("Vector<Integer>"))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("Vector<String>"))
                    {
                        result += "null,";
                    }
                    else if(current2.contains("Vector<"))
                    {
                        result += "null,";
                    }
                    else 
                    {
                        result += " ";
                    }
                }
            }
            else
            {
                
            }
        }
        return result;
    }
    
    /**
     * Checks if the class has a constructor
     * @param file
     * @return 
     */
    public boolean hasConstructor(String file)
    {
        Scanner scanner = new Scanner(file);
        boolean result = false;
        while(scanner.hasNext())
        {
            String currentLine = scanner.nextLine();
            //Class doesnt count as a constructor
            if(currentLine.contains(" class "))
            {
                
            }
            //If the superClassName is found not on the class line then it must have a constructor
            else if(currentLine.contains(" " + superClassName))
            {
                result = true;
                return result;
            }
        }
        return result;
    }
}

